#pragma once

#include <set>
#include <map>
#include <string>

#include <ConnectionBroker/Authenticator.h>
#include <ConnectionBroker/Channel.h>
#include <ConnectionBroker/Packet.h>
#include <ConnectionBroker/Typedefs.h>
#include <ConnectionBroker/clientconnection/ClientConnection.h>
#include <ConnectionBroker/server/Server.h>
#include <boost/asio/ssl.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>

namespace ConnectionBroker
{
   class Application : public boost::enable_shared_from_this<Application>
   {

      protected: //Member variables
      AuthenticatorPtr mAuthenticator;
      std::set<ServerPtr> mServers;
      std::set<boost::shared_ptr<boost::thread> > mThreads;
      std::map<std::string, ChannelPtr> mRegisteredChannels; 
      uint32_t mNextRegisteredChannel;
      applicationCallback mOnClientConnectCallback;
      applicationCallback mOnClientDisconnectCallback;
      applicationCallback mOnMessageCallback;
      std::string mVersion;
      boost::shared_ptr<boost::asio::ssl::context> mContext;
      

      public: //Constructor, destructor
      Application();
      virtual ~Application();

      public: //Factories
      ServerPtr longPollServerFactory(const ServerSettings& settings); 
      ServerPtr tcpServerFactory(const ServerSettings& settings); 
      ServerPtr webSocketServerFactory(const ServerSettings& settings);

      public: //Getters
      const std::string& version() const { return mVersion; }

      public: //Public Interface
      uint32_t channelNumber(const std::string& channelName);
      uint32_t numberOfClientsSubscribedTo(const std::string& channelName);
      void removeChannel(const std::string& name);
      void registerChannel(const std::string& name, applicationCallback pChannelCallback = NULL);
      void removeOnMessageCallback(); 
      void removeServerOnMessageCallback(ServerPtr pServer); 
      void setAuthenticator(AuthenticatorPtr pAuthenticator);
      void setOnClientConnectCallback(applicationCallback pCallback);
      void setServerOnClientConnectCallback(ServerPtr pServer, applicationCallback pCallback);
      void setOnClientDisconnectCallback(applicationCallback pCallback);
      void setServerOnClientDisconnectCallback(ServerPtr pServer, applicationCallback pCallback);
      void setOnMessageCallback(applicationCallback pCallback); 
      void setServerOnMessageCallback(ServerPtr pServer, applicationCallback pCallback); 
      void setSslInfo(const std::string& certChainFile, const std::string& privateKeyFile, const std::string& tmpDhFile);
      void writeToAll(const Packet& pMessage);

      protected: //Utilities
      Packet packAuthenticated();
      Packet packVersion();
      Packet packChannelList();
      ChannelPtr channelFromNumber(const uint32_t channelNumber);
      void handleAuthentication(ClientConnectionPtr pClient, const Packet& pMessage);
      void handleChannelSubscription(ClientConnectionPtr pClient, const Packet& pMessage);
      void invokeOnClientConnectCallback(ServerPtr pServer, ClientConnectionPtr pClient, const Packet& pMessage);
      void invokeOnClientDisconnectCallback(ServerPtr pServer, ClientConnectionPtr pClient, const Packet& pMessage);
      void invokeOnMessageCallback(ServerPtr pServer, ClientConnectionPtr pClient, const Packet& pMessage);
   };
   //typedef boost::shared_ptr<Application> ApplicationPtr;
}

